/**
 * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 */

package com.iwindplus.boot.mail.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.iwindplus.boot.domain.constant.CommonConstant.ExceptionConstant;
import com.iwindplus.boot.mail.domain.dto.MailDTO;
import com.iwindplus.boot.mail.domain.vo.MailVO;
import com.iwindplus.boot.mail.service.MailService;
import com.iwindplus.boot.util.NetsUtil;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;

/**
 * 邮箱业务层接口实现类.
 *
 * @author zengdegui
 * @since 2018/9/1
 */
@Slf4j
public class MailServiceImpl extends AbstractMailBaseServiceImpl implements MailService {

    @Override
    public MailVO send(MailDTO entity) {
        if (entity == null || CollUtil.isEmpty(entity.getTos())) {
            log.warn("邮件实体为空或收件人为空");
            return MailVO.builder().result(Boolean.FALSE).errorMsg("邮件实体为空或收件人为空").build();
        }

        // 同步发送
        if (!this.getConfig().getEnableRetry()) {
            return this.sendMail(entity);
        } else {
            return this.retrySendMail(entity);
        }
    }

    /**
     * 单次发送邮件
     */
    private MailVO sendMail(MailDTO entity) {
        MailVO result = MailVO.builder().result(Boolean.TRUE).sendCount(1).bizNumber(this.getBizNumber(entity)).build();

        try {
            this.sendMailMessage(entity);
            return result;
        } catch (Exception ex) {
            String errorMsg = String.format("邮件发送失败 [to: %s, subject: %s] - %s",
                String.join(",", entity.getTos()), entity.getSubject(), ex.getMessage());
            log.error(errorMsg, ex);
            result.setResult(Boolean.FALSE);
            result.setErrorMsg(errorMsg);
            return result;
        }
    }

    private String getBizNumber(MailDTO entity) {
        String tosString = String.join(",", entity.getTos());
        String param = new StringBuilder(tosString)
            .append(entity.getSubject())
            .append(entity.getContent())
            .toString();
        return DigestUtil.md5Hex(param);
    }

    /**
     * 带重试机制的邮件发送
     */
    private MailVO retrySendMail(MailDTO entity) {
        MailVO result = MailVO.builder().result(Boolean.TRUE).sendCount(1).bizNumber(this.getBizNumber(entity)).build();
        AtomicInteger attempt = new AtomicInteger(1);
        int maxAttempts = this.getConfig().getMaxAttempts();

        while (true) {
            try {
                this.sendMailMessage(entity);
                result.setSendCount(attempt.get());
                return result;
            } catch (Exception ex) {
                String errorMsg = String.format("邮件发送失败（第%d次重试）[to: %s, subject: %s]",
                    attempt.get(), String.join(",", entity.getTos()), entity.getSubject());

                log.error("{} - {}", errorMsg, ex.getMessage());

                if (attempt.get() >= maxAttempts) {
                    String finalErrorMsg = errorMsg + "，超过最大重试次数";
                    log.error(finalErrorMsg, ex);

                    result.setResult(Boolean.FALSE);
                    result.setSendCount(attempt.get());
                    result.setErrorMsg(finalErrorMsg);
                    return result;
                } else {
                    syncRetryWait(attempt);
                }
            }
        }
    }

    /**
     * 发送邮件核心逻辑
     */
    private void sendMailMessage(MailDTO entity) throws Exception {
        JavaMailSenderImpl sender = getOrCreateMailSender();
        MimeMessage message = sender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true, this.getConfig().getDefaultEncoding().name());

        // 设置收件人
        helper.setTo(entity.getTos().toArray(String[]::new));
        if (CollUtil.isNotEmpty(entity.getCcs())) {
            helper.setCc(entity.getCcs().toArray(String[]::new));
        }
        if (CollUtil.isNotEmpty(entity.getBccs())) {
            helper.setBcc(entity.getBccs().toArray(String[]::new));
        }

        // 设置发件人
        InternetAddress from = new InternetAddress(sender.getUsername());
        if (CharSequenceUtil.isNotBlank(this.getConfig().getNickName())) {
            from.setPersonal(this.getConfig().getNickName(), sender.getDefaultEncoding());
        }
        helper.setFrom(from);

        // 设置主题和内容
        helper.setSubject(entity.getSubject());
        helper.setText(entity.getContent(), Optional.ofNullable(entity.getHtml()).orElse(false));

        // 添加附件
        addAttachments(helper, entity.getAttachments());

        // 发送
        sender.send(message);
    }

    /**
     * 获取或创建邮件发送器实例（复用）
     */
    private JavaMailSenderImpl getOrCreateMailSender() {
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost(CharSequenceUtil.trimToNull(this.getConfig().getHost()));
        if (Objects.nonNull(this.getConfig().getPort())) {
            mailSender.setPort(this.getConfig().getPort());
        }
        mailSender.setUsername(CharSequenceUtil.trimToNull(this.getConfig().getUsername()));
        mailSender.setPassword(CharSequenceUtil.trimToNull(this.getConfig().getPassword()));
        mailSender.setProtocol(CharSequenceUtil.trimToNull(this.getConfig().getProtocol()));
        mailSender.setDefaultEncoding(CharSequenceUtil.trimToNull(this.getConfig().getDefaultEncoding().name()));

        if (MapUtil.isNotEmpty(this.getConfig().getProperties())) {
            Properties props = new Properties();
            this.getConfig().getProperties().forEach(props::put);
            mailSender.setJavaMailProperties(props);
        }
        return mailSender;
    }

    /**
     * 添加附件
     */
    private void addAttachments(MimeMessageHelper helper, List<MailDTO.Attachment> attachments) {
        if (CollUtil.isEmpty(attachments)) {
            return;
        }
        for (MailDTO.Attachment attachment : attachments) {
            String filename = attachment.getSourceFileName();
            byte[] data = attachment.getData();
            if (data == null || data.length == 0) {
                log.warn("附件 [{}] 数据为空，跳过添加", filename);
                continue;
            }
            try {
                helper.addAttachment(filename, new ByteArrayResource(data));
            } catch (Exception ex) {
                log.error("添加附件 [{}] 失败", filename, ex);
            }
        }
    }

    /**
     * 同步重试等待
     */
    private void syncRetryWait(AtomicInteger attempt) {
        int count = attempt.getAndIncrement();
        long interval = NetsUtil.nextMaxInterval(
            this.getConfig().getPeriod().toMillis(),
            this.getConfig().getMaxPeriod().toMillis(),
            count
        );
        try {
            log.warn("邮件发送失败，{} 毫秒后重试（第{}次）", interval, count);
            Thread.sleep(interval);
        } catch (InterruptedException ignored) {
            Thread.currentThread().interrupt();

            log.error(ExceptionConstant.INTERRUPTED_EXCEPTION);
        }
    }

}
